package com.study.mongodbdynamicdatasource.config;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @Name: MongoDbContext
 * @Description: java类作用描述
 * @Author: yn
 * @CreateDate: 2023/6/9 15:28
 * @Remark: 特殊说明
 * @Version: 1.0
 */
@Slf4j
public class MongoDbContext {
    private static final Map<String, MongoDatabaseFactory> MONGO_CLIENT_DB_FACTORY_MAP = new ConcurrentHashMap<>();
    //栈实现
    //有注解不走多数据源
    //mysql取数据源
    /**
     * 默认数据源
     */
    private static MongoDatabaseFactory DEFAULT_MONGO_DATABASE_FACTORY;
    private static AtomicBoolean closed=new AtomicBoolean(false);

    /**
     * 设置默认数据源
     * @param mongoDatabaseFactory
     */
    public static synchronized void setDefaultMongoDatabaseFactory(MongoDatabaseFactory mongoDatabaseFactory) throws Exception {
        Assert.isFalse(closed.get(),()->new Exception("默认数据源已经初始化,不能再次设置"));
        DEFAULT_MONGO_DATABASE_FACTORY = mongoDatabaseFactory;
        closed.compareAndSet(false,true);
    }
    private static final ThreadLocal<Stack<MongoDatabaseFactory>> MONGO_DB_FACTORY_THREAD_LOCAL =
            new ThreadLocal<>();
    private static final ThreadLocal<Boolean> NOT_SWITCH_DYNAMICALLY_IDENTITIES =
            new ThreadLocal<>();

    public static MongoDatabaseFactory getMongoDbFactory() {
        if (Objects.isNull(MONGO_DB_FACTORY_THREAD_LOCAL.get())) {
            return DEFAULT_MONGO_DATABASE_FACTORY;
        }
        return MONGO_DB_FACTORY_THREAD_LOCAL.get().peek();
    }

    /**
     * 设置数据源
     * @param name 数据源对应的名称（MongoDbContext.DEFAULT/tenantId）
     */
    public static void pushMongoDatabaseFactory(String name) {
        if (Objects.nonNull(NOT_SWITCH_DYNAMICALLY_IDENTITIES.get())){
            return;
        }
        //要切换的数据源在Map中取出对应的数据源
        if(StrUtil.isNotBlank(name) && MONGO_CLIENT_DB_FACTORY_MAP.containsKey(name)){
            doPushMongoDatabaseFactory(MONGO_CLIENT_DB_FACTORY_MAP.get(name));
        }
        //不在使用默认的数据源
        else{
            doPushMongoDatabaseFactory(DEFAULT_MONGO_DATABASE_FACTORY);
        }
    }

    public static void setNotSwitchDynamicallyIdentities(){
        NOT_SWITCH_DYNAMICALLY_IDENTITIES.set(true);
    }
    public static void setSwitchDynamicallyIdentities(){
        NOT_SWITCH_DYNAMICALLY_IDENTITIES.remove();
    }
    /**
     * 切换数据源
     * @param mongoDatabaseFactory
     */
    private static void doPushMongoDatabaseFactory(MongoDatabaseFactory mongoDatabaseFactory){
        if(Objects.isNull(MONGO_DB_FACTORY_THREAD_LOCAL.get())){
            MONGO_DB_FACTORY_THREAD_LOCAL.set(new Stack<>());
        }
        if (CollectionUtil.isEmpty(MONGO_DB_FACTORY_THREAD_LOCAL.get()) || !Objects.equals(MONGO_DB_FACTORY_THREAD_LOCAL.get().peek(),mongoDatabaseFactory)) {
            MONGO_DB_FACTORY_THREAD_LOCAL.get().push(mongoDatabaseFactory);
            log.info("切换数据源成功:{}/{}",mongoDatabaseFactory.getMongoDatabase().getName(),MONGO_DB_FACTORY_THREAD_LOCAL.get().size());
        }
    }

    /**
     * 移除当前数据源
     */
    public static void removeMongoDbFactory() {
        MONGO_DB_FACTORY_THREAD_LOCAL.get().pop();
        System.out.println("移除当前数据源");
    }

    /**
     * 清空数据源
     */
    public static void clearMongoDbFactory() {
        MONGO_DB_FACTORY_THREAD_LOCAL.remove();
        System.out.println("清空数据源");
    }

    /**
     * 加载MongoDbFactory
     * @param dblist 数据源信息
     */
    public static void loadMongoClientDbFactory(List<MongoList> dblist) {
        dblist
                .forEach(
                        info -> {
                            MONGO_CLIENT_DB_FACTORY_MAP.put(
                                    info.getDatabase(), new SimpleMongoClientDatabaseFactory(info.getUri()));
                        });
    }
}
